#!/bin/bash/env python

# Enemies galore. I haven't decided if this should be classed or just be a module. TBD.

from panda3d.core import CollisionTraverser,CollisionNode
from panda3d.core import CollisionHandlerQueue,CollisionSphere,CollisionRay
from panda3d.core import BitMask32
from panda3d.core import PandaNode,NodePath,Camera,TextNode
from direct.interval.IntervalGlobal import *

""" Handles enemy initialization and enemy collision handling. """
# --------------------
def initEnemies(self, level):
# --------------------
    # -------------
    # ENEMY LOADING
    # -------------
    self.goombae = {} # a record is: {int: ( eggmodel, direction, alive, lastHit )}, 
                          # where direction = -1 for "toward start"
                          # where alive = True for yes, False for no
                          # where lastHit is a string describing the last wall collision
    self.koopae = {}  # a record is: {int: ( (koopamodel, shellmodel), direction, alive, lastHit )},
                      # where direction = -1 for "toward start",
                      # where status = 1 for well, 0 for shell, -1 for dead
                      # where lastHit is a string describing the last wall collision
    self.enemies = (self.goombae, self.koopae)
    self.goombaSX = .25
    self.goombaSY = .25
    self.goombaSZ = .25
    
    if "1-1" in level:
        #Load the Goombae
        self.goombae[0] = (loader.loadModel("../actors/goomba/goomba"), -1, True, "nothing")
        enemy = self.goombae[0][0]
        enemy.reparentTo(render)
        enemy.setPos(0, 32, 1)
        enemy.setScale(self.goombaSX, self.goombaSY, self.goombaSZ)
        #enemy.setHpr(90, 90, 90)
        
        self.goombae[1] = (loader.loadModel("../actors/goomba/goomba"), -1, True, "nothing")
        enemy = self.goombae[1][0]
        enemy.reparentTo(render)
        enemy.setPos(0, 60, 1)
        enemy.setHpr(180, 0, 0)
        enemy.setScale(self.goombaSX, self.goombaSY, self.goombaSZ)
        
        self.goombae[2] = (loader.loadModel("../actors/goomba/goomba"), -1, True, "nothing")
        enemy = self.goombae[1][0]
        enemy.reparentTo(render)
        enemy.setPos(0, 140, 1)
        enemy.setHpr(180, 0, 0)
        enemy.setScale(self.goombaSX, self.goombaSY, self.goombaSZ)
        
        #Load the Koopae
        self.koopae[0] = ((loader.loadModel("../actors/koopa/koopa"), 
                loader.loadModel("../actors/koopa/shell")), 1, 1, "nothing")
        shell = self.koopae[0][0][1]
        shell.reparentTo(render)
        koopa = self.koopae[0][0][0]
        koopa.reparentTo(shell)
        shell.setPos(0, 66, 1)
        shell.setScale(-1*self.goombaSX, self.goombaSY+.1, self.goombaSZ)
        koopa.setPos(0, 0, 0)
        koopa.setScale(-1, 1, 1)
        koopa.setHpr(180, 0, 0)
        
        self.koopae[1] = ((loader.loadModel("../actors/koopa/koopa"), 
                loader.loadModel("../actors/koopa/shell")), 1, 1, "nothing")
        shell = self.koopae[1][0][1]
        shell.reparentTo(render)
        koopa = self.koopae[1][0][0]
        koopa.reparentTo(shell)
        shell.setPos(0, 83, 1)
        shell.setScale(-1*self.goombaSX, self.goombaSY+.1, self.goombaSZ)
        koopa.setPos(0, 0, 0)
        koopa.setScale(-1, 1, 1)
        koopa.setHpr(180, 0, 0)
        
    elif level is 2:
        #Load the Goombae
        self.goombae[0] = (loader.loadModel("../actors/goomba/goomba"), -1, True, "nothing")
        enemy = self.goombae[0][0]
        enemy.reparentTo(render)
        enemy.setPos(0, 32, 1)
        enemy.setScale(self.goombaSX, self.goombaSY, self.goombaSZ)
        #enemy.setHpr(90, 90, 90)
        
        self.goombae[1] = (loader.loadModel("../actors/goomba/goomba"), -1, True, "nothing")
        enemy = self.goombae[1][0]
        enemy.reparentTo(render)
        enemy.setPos(0, 60, 1)
        enemy.setHpr(180, 0, 0)
        enemy.setScale(self.goombaSX, self.goombaSY, self.goombaSZ)
        
        self.goombae[2] = (loader.loadModel("../actors/goomba/goomba"), -1, True, "nothing")
        enemy = self.goombae[1][0]
        enemy.reparentTo(render)
        enemy.setPos(0, 140, 1)
        enemy.setHpr(180, 0, 0)
        enemy.setScale(self.goombaSX, self.goombaSY, self.goombaSZ)
        self.koopae[0] = ((loader.loadModel("../actors/koopa/koopa"), 
                loader.loadModel("../actors/koopa/shell")), 1, 1, "nothing")
        shell = self.koopae[0][0][1]
        shell.reparentTo(render)
        koopa = self.koopae[0][0][0]
        koopa.reparentTo(shell)
        shell.setPos(0, 66, 1)
        shell.setScale(-1*self.goombaSX, self.goombaSY+.1, self.goombaSZ)
        koopa.setPos(0, 0, 0)
        koopa.setScale(-1, 1, 1)
        koopa.setHpr(180, 0, 0)
        
        #Load the Koopae
        self.koopae[1] = ((loader.loadModel("../actors/koopa/koopa"), 
                loader.loadModel("../actors/koopa/shell")), 1, 1, "nothing")
        shell = self.koopae[1][0][1]
        shell.reparentTo(render)
        koopa = self.koopae[1][0][0]
        koopa.reparentTo(shell)
        shell.setPos(0, 83, 1)
        shell.setScale(-1*self.goombaSX, self.goombaSY+.1, self.goombaSZ)
        koopa.setPos(0, 0, 0)
        koopa.setScale(-1, 1, 1)
        koopa.setHpr(180, 0, 0)

        
    ### General Initialization ###
    
    # define each goomba's movement
    for key, enemyRecord in self.enemies[0].items():
        #define first interval
        Sequence(LerpPosInterval(enemyRecord[0], 1,
                goombaTarget(self, key, enemyRecord[0].getPos())),
                Func(nextGoombaMove, self, key, goombaTarget(self, key,
                enemyRecord[0].getPos()))).start()
                
    # define each koopa's movement
    for key, enemyRecord in self.enemies[1].items():
        #define first interval
        Sequence(LerpPosInterval(enemyRecord[0][1], 1,
                koopaTarget(self, key, enemyRecord[0][1].getPos())), 
                Func(nextKoopaMove, self, key, koopaTarget(self, key, enemyRecord[0][1].getPos()))).start()



    # ------------------------
    # ENEMY COLLISION HANDLING
    # ------------------------

    self.enemySphereHandler = CollisionHandlerQueue()
    for key, enemyRecord in self.enemies[0].items():
        self.goombaSphereColNp = setupEnemySphereCollider(self, enemyRecord[0], 0, 0, 4, 3,
                'enemy/goombaSphere'+'#'+str(key), self.enemySphereHandler, 0)

    self.koopaSphereHandler = CollisionHandlerQueue()
    for key, enemyRecord in self.enemies[1].items():
        self.koopaSphereColNp = setupEnemySphereCollider(self, enemyRecord[0][1], 0, 0, 4, 3.5,
                'enemy/koopaSphere'+'#'+str(key), self.enemySphereHandler, 1)

# -----------------------
def collideEnemiesIntoRalph(self, entry):
# -----------------------
    nodePath = entry.getFromNodePath()
    if "goomba" in nodePath.getName():
        goomba_index = int(nodePath.getName()[nodePath.getName().find('#')+1:])
        
        if self.upSpeed < 0 :
                    
            if self.goombae[goomba_index][2] is True:
                nodePath.detachNode() #removes collision
                self.goombae[goomba_index][0].detachNode() #removes render object
                self.goombae[goomba_index] = (self.goombae[goomba_index][0],
                                             self.goombae[goomba_index][1], 
                                             False, 
                                             self.goombae[goomba_index][3]) 
                
                #bounce off goomba
                if self.DEBUGTEXT: text_writers.setInst(self, 2, "JUMPING: {0}".format(self.keyMap["jumping"]))
                self.upSpeed = self.JUMPVECTOR
                if self.DEBUGTEXT: text_writers.setInst(self, 1, "upSpeed: {0}".format(self.upSpeed))
                self.jumpSound.play()          
                self.jumpTime = 0
                self.setKey("jumping", 1)
                    
        else:
            if self.goombae[goomba_index][2] is True:
                nodePath.detachNode()#removes collision
                self.die()
                
            #else:
            #   we have a phantom Goomba
    elif "koopa" in nodePath.getName():
        koopa_index = int(nodePath.getName()[nodePath.getName().find('#')+1:])
       
        if self.upSpeed < 0 :
            print "Hit Koopa #" + str(koopa_index)
                
            if self.koopae[koopa_index][2] is 1:
                #bounce off koopa
                if self.DEBUGTEXT: text_writers.setInst(self, 2, "JUMPING: {0}".format(self.keyMap["jumping"]))
                self.upSpeed = self.JUMPVECTOR
                if self.DEBUGTEXT: text_writers.setInst(self, 1, "upSpeed: {0}".format(self.upSpeed))
                self.jumpSound.play()          
                self.jumpTime = 0
                self.setKey("jumping", 1)
                
                #nodePath.detachNode() #removes collision
                self.koopae[koopa_index][0][0].detachNode() #removes render object: body
                self.koopae[koopa_index] = (self.koopae[koopa_index][0], 
                                            self.koopae[koopa_index][1], 
                                            self.koopae[koopa_index][2]-1, 
                                            self.koopae[koopa_index][3])
                
            elif self.koopae[koopa_index][2] is 0:
                #bounce off shell
                if self.DEBUGTEXT: text_writers.setInst(self, 2, "JUMPING: {0}".format(self.keyMap["jumping"]))
                self.upSpeed = self.JUMPVECTOR
                if self.DEBUGTEXT: text_writers.setInst(self, 1, "upSpeed: {0}".format(self.upSpeed))
                self.jumpSound.play()          
                self.jumpTime = 0
                self.setKey("jumping", 1)
                
                #nodePath.detachNode() #removes collision
                #self.koopae[koopa_index][0][1].detachNode() #removes render object: shell
            #else:
            #   we have a phantom Koopa
        else:
            if self.koopae[koopa_index][2] > -1 and (self.ralph.getZ() - self.koopae[koopa_index][0][1].getZ()) < .5:
                nodePath.detachNode()
                self.die()
            #else:
            #   we have a phantom Koopa


# ------------------------------------------
def collideEnemiesIntoWall(self, entry):
# ------------------------------------------
    #print entry.getFromNodePath().getName() + " hit " + entry.getIntoNodePath().getName()
    fromNodePath = entry.getFromNodePath()
    if "goomba" in fromNodePath.getName() :
        enemy_type = 0
    elif "koopa" in fromNodePath.getName() :
        enemy_type = 1
        
    enemy_index = int(fromNodePath.getName()[fromNodePath.getName().find('#')+1:])
    
    model, direction, alive, lastHit = self.enemies[enemy_type][enemy_index]
    
    if lastHit not in entry.getIntoNodePath().getName() :
    
        model, direction, alive, lastHit = self.enemies[enemy_type][enemy_index]

        if enemy_type is 1:
                    newModelBody, newModelShell  = model
                    y, p, r = newModelShell.getHpr()
                    newModelShell.setHpr((180+y) % 360, p, r)
        elif enemy_type is 0:
            newModelBody = model
                
        y, p, r = newModelBody.getHpr()
        newModelBody.setHpr((180+y) % 360, p, r)
        
        if enemy_type is 1:
            newModel = (newModelBody, newModelShell)
        elif enemy_type is 0:
            newModel = newModelBody
        
        self.enemies[enemy_type][enemy_index] = ( newModel, direction * -1, alive, entry.getIntoNodePath().getName())#offenderName)
        
        if enemy_type is 1 and self.koopae[enemy_index][2] is 0 :
            entry.getIntoNodePath().getParent().hide() # hide block
            entry.getIntoNodePath().detachNode() # remove collision
            
            
# ------------------------------------------
def collideEnemiesIntoEnemies(self, entry):
# ------------------------------------------
    fromNodePath = entry.getFromNodePath()
    intoNodePath = entry.getIntoNodePath()
    
    from_index = int(fromNodePath.getName()[fromNodePath.getName().find('#')+1:])
    if "goomba" in fromNodePath.getName() :
        from_type = 0
    elif "koopa" in fromNodePath.getName() :
        from_type = 1
        
    into_index = int(intoNodePath.getName()[intoNodePath.getName().find('#')+1:])
    if "goomba" in intoNodePath.getName() :
        into_type = 0
    elif "koopa" in intoNodePath.getName() :
        into_type = 1
    
    if from_type is 0 and into_type is 1: return
    
    if self.enemies[from_type][from_index][3] not in intoNodePath.getName() :
    
        model, direction, alive, lastHit = self.enemies[from_type][from_index]

        if from_type is 1:
                    newModelShell, newModelBody = model
                    y, p, r = newModelShell.getHpr()
                    newModelShell.setHpr((180+y) % 360, p, r)
        elif from_type is 0:
            newModelBody = model
                
        y, p, r = newModelBody.getScale()
        newModelBody.setHpr((180+y) % 360, p, r)
        
        if from_type is 1:
            newModel = (newModelShell, newModelBody)
        elif from_type is 0:
            newModel = newModelBody
        
        self.enemies[from_type][from_index] = ( newModel, direction * -1, alive, intoNodePath.getName())#offenderName)
        
    if self.enemies[into_type][into_index][3] not in fromNodePath.getName() :

        model, direction, alive, lastHit = self.enemies[into_type][into_index]

        if into_type is 1:
                    newModelShell, newModelBody = model
                    y, p, r = newModelShell.getHpr()
                    newModelShell.setHpr((180+y) % 360, p, r)
        elif into_type is 0:
            newModelBody = model
                
        y, p, r = newModelBody.getScale()
        newModelBody.setHpr((180+y) % 360, p, r)
        
        if into_type is 1:
            newModel = (newModelShell, newModelBody)
        elif into_type is 0:
            newModel = newModelBody
        
        self.enemies[into_type][into_index] = ( newModel, direction * -1, alive, fromNodePath.getName())
    #turnEnemyAround(self, into_index, into_type, fromNodePath.getName())
    
    if from_type is 1 :
        model, direction, alive, lastHit = self.enemies[1][from_index]
        
        if alive is 0 :
            #entry.getIntoNodePath().getParent().hide() # kill goomba
            entry.getIntoNodePath().detachNode() # rem collision
            
            victim_index = int(entry.getIntoNodePath().getName()[entry.getIntoNodePath().getName().find('#')+1:])
            if "goomba" in entry.getIntoNodePath().getName() :
                enemy_type = 0
                self.goombae[victim_index][0].detachNode()
                self.goombae[victim_index] = (self.enemies[victim_index][0],
                                                         self.enemies[enemy_type][victim_index][1], 
                                                         False, 
                                                         self.enemies[enemy_type][victim_index][3]) 
                
            elif "koopa" in entry.getIntoNodePath().getName() :
                enemy_type = 1
                if self.koopae[victim_index][2] is 1 :
                    self.enemies[enemy_type][victim_index][0][0].detachNode()
                    self.enemies[enemy_type][victim_index][0][1].detachNode()
                    self.enemies[enemy_type][victim_index] = (self.enemies[victim_index][0],
                                                             self.enemies[enemy_type][victim_index][1], 
                                                             -1, 
                                                             self.enemies[enemy_type][victim_index][3]) 
            else :
                print "WTF???"
    

# -----------------------------------------------------------------------
def setupEnemySphereCollider(self, target, x, y, z, r, name, handler, type):
# -----------------------------------------------------------------------
    sphere = CollisionSphere(x, y, z, r)
    sphereCol = CollisionNode(name)
    sphereCol.addSolid(sphere)
    sphereCol.setFromCollideMask(BitMask32 (0x06))
    sphereCol.setIntoCollideMask(BitMask32 (0x04))
    #if type is 1: #koopae can't be hit into by enemies
    #    sphereCol.setIntoCollideMask(BitMask32.allOff())
    #else :
    #    sphereCol.setIntoCollideMask(BitMask32 (0x04))
        
    np = target.attachNewNode(sphereCol)
    self.cTrav.addCollider(np, handler)
    
    return np

""" Enemy Movement """
# ------------------------------------------
def goombaTarget(self, goomba_index, point3):
# ------------------------------------------
    goombaRecord = self.goombae[goomba_index]
    direction = goombaRecord[1]
    return ( point3[0], point3[1] + 3.0*direction/30, point3[2] )

# -------------------------------------------------
def nextGoombaMove(self, goomba_index, moveTarget):
# -------------------------------------------------
    newTarget = goombaTarget(self, goomba_index, moveTarget)
    if self.goombae[goomba_index][2] is True :
        Sequence(LerpPosInterval(self.goombae[goomba_index][0], 1/30, newTarget),
                Func(nextGoombaMove, self, goomba_index, newTarget)).start()
    
# ------------------------------------------
def koopaTarget(self, koopa_index, point3):
# ------------------------------------------
    koopaRecord = self.koopae[koopa_index]
    direction = koopaRecord[1]
    speed = 1 - koopaRecord[2]
    return ( point3[0], point3[1] + 3.0/30*direction + 3*speed/30.0*direction, point3[2] )

# ------------------------------------------
def koopaSpin(self, koopa_index):
# ------------------------------------------
    koopaRecord = self.koopae[koopa_index]
    if self.koopae[koopa_index][2] > -1 :
        y, p, r = koopaRecord[0][1].getHpr()
        if koopaRecord[2] is 0 :
            y = (y+180/30)%360
        return (y, p, r)
    else :
        return (0, 0, 0)
    
# -------------------------------------------------
def nextKoopaMove(self, koopa_index, moveTarget):
# -------------------------------------------------
    newTarget = koopaTarget(self, koopa_index, moveTarget)
    
    #if self.koopae[koopa_index][2] is 0 :
    #if self.koopae[koopa_index]
    newHpr = koopaSpin(self, koopa_index)
                
    if self.koopae[koopa_index][2] > -1 :
        Sequence(
                Parallel(LerpPosInterval(self.koopae[koopa_index][0][1], 1/30, newTarget),
                        LerpHprInterval(self.koopae[koopa_index][0][1], 1/30, newHpr)),
                Func(nextKoopaMove, self, koopa_index, newTarget)).start()
                
# -------------------------------------------------
def turnEnemyAround(self, enemy_index, enemy_type, offenderName):
# -------------------------------------------------
    model, direction, alive, lastHit = self.enemies[enemy_type][enemy_index]

    if enemy_type is 1:
                newModelShell, newModelBody = model
                y, p, r = newModelShell.getHpr()
                newModelShell.setHpr((180+y) % 360, p, r)
    elif enemy_type is 0:
        newModelBody = model
            
    y, p, r = newModelBody.getScale()
    newModelBody.setHpr((180+y) % 360, p, r)
    
    if enemy_type is 1:
        newModel = (newModelShell, newModelBody)
    elif enemy_type is 0:
        newModel = newModelBody
    
    self.enemies[enemy_type][enemy_index] = ( newModel, direction * -1, alive, offenderName)